D:\a\tools.proto\tools.proto\compiler\src\gen\base\message.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::message::{Field, FieldType, Message, Referenced}; |
30 | | use crate::compiler::structure::FixedFieldType; |
31 | | use crate::compiler::util::types::TypeMapper; |
32 | | use crate::gen::base::map::TypePathMapper; |
33 | | use crate::gen::base::Error; |
34 | | use crate::gen::codec::CodecMap; |
35 | | use crate::gen::template::Template; |
36 | | use crate::model::protocol::Endianness; |
37 | | |
38 | | pub trait Utilities: crate::gen::base::structure::Utilities { |
39 | | fn get_value_type(endianness: Endianness, ty: FixedFieldType) -> &'static str; |
40 | | fn get_value_type_inline(endianness: Endianness, ty: FixedFieldType) -> &'static str; |
41 | | fn gen_struct_ref_type(type_name: &str) -> String; |
42 | | fn gen_message_ref_type(type_name: &str) -> String; |
43 | | } |
44 | | |
45 | | pub struct Templates<'fragment, 'variable> { |
46 | | pub template: Template<'fragment, 'variable>, |
47 | | pub codec_map: &'fragment CodecMap<'fragment, 'variable>, |
48 | | } |
49 | | |
50 | | impl<'fragment, 'variable> Templates<'fragment, 'variable> { |
51 | 227 | pub fn get(&self, codec: &str) -> Result<&Template<'fragment, 'variable>, Error> { |
52 | 227 | Ok(self.codec_map.get(codec).ok_or_else(|| Error::CodecNotFound(codec.into())0 )?0 ) |
53 | 227 | } |
54 | | } |
55 | | |
56 | 49 | pub fn gen_msg_field_decl<U: Utilities, T: TypeMapper>( |
57 | 49 | field: &Field, |
58 | 49 | templates: &Templates, |
59 | 49 | type_path_map: &TypePathMapper<T>, |
60 | 49 | ) -> Result<String, Error> { |
61 | 49 | let codec_template = templates.get(field.codec())?0 ; |
62 | 49 | let mut scope = templates.template.scope(); |
63 | 49 | scope.var("name", &field.name).var("description", "").var_d("info", field); |
64 | 49 | let msg_type = match &field.ty { |
65 | 4 | FieldType::Fixed(ty) => U::get_field_type(ty.ty).into(), |
66 | 11 | FieldType::Ref(v) => match v { |
67 | 9 | Referenced::Struct(v) => U::gen_struct_ref_type(&type_path_map.get(v)), |
68 | 2 | Referenced::Message(v) => U::gen_message_ref_type(&type_path_map.get(v)), |
69 | | }, |
70 | 10 | FieldType::Buffer => codec_template.render("decl", &["buffer"]).map_err(Error::Codec)?0 , |
71 | 4 | FieldType::SizedBuffer(v) => codec_template |
72 | 4 | .scope() |
73 | 4 | .var("codec", U::get_value_type(field.endianness, v.ty)) |
74 | 4 | .render("decl", &["sized_buffer"]) |
75 | 4 | .map_err(Error::Codec)?0 , |
76 | 4 | FieldType::FixedContainer(_) => scope.render("", &["list"]).unwrap(), |
77 | 4 | FieldType::Union(v) => scope.var("type_name", type_path_map.get(&v.r)).render("", &["union"]).unwrap(), |
78 | 9 | FieldType::Container(_) => scope.render("", &["list"]).unwrap(), |
79 | 0 | FieldType::Payload => codec_template.render("decl", &["payload"]).map_err(Error::Codec)?, |
80 | 3 | FieldType::SizedContainer(_) => scope.render("", &["list"]).unwrap(), |
81 | | }; |
82 | 49 | let msg_type = match field.optional { |
83 | 7 | true => codec_template.scope().var("msg_type", msg_type).render("decl", &["option"]).map_err(Error::Codec)?0 , |
84 | 42 | false => msg_type, |
85 | | }; |
86 | 49 | Ok(scope.var("type", msg_type).render("decl", &["field"]).unwrap()) |
87 | 49 | } |
88 | | |
89 | 57 | fn gen_message_array_field<U: Utilities, T: TypeMapper>( |
90 | 57 | templates: &Templates, |
91 | 57 | function: &str, |
92 | 57 | field: &Field, |
93 | 57 | type_path_map: &TypePathMapper<T>, |
94 | 57 | ) -> Result<Option<String>, Error> { |
95 | 57 | let mut scope = templates.template.scope(); |
96 | 57 | let codec_template = templates.get(field.codec())?0 ; |
97 | 57 | scope |
98 | 57 | .var("name", &field.name) |
99 | 57 | .var( |
100 | 57 | "description", |
101 | 57 | field.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
102 | 57 | ) |
103 | 57 | .var_d("info", field); |
104 | 57 | let value = match &field.ty { |
105 | 6 | FieldType::FixedContainer(v) => { |
106 | 6 | scope |
107 | 6 | .var("item_type", type_path_map.get(&v.item_type)) |
108 | 6 | .var("codec", U::get_value_type(field.endianness, v.ty)); |
109 | 6 | let type_name = codec_template |
110 | 6 | .scope() |
111 | 6 | .var("item_type", type_path_map.get(&v.item_type)) |
112 | 6 | .var("codec", U::get_value_type(field.endianness, v.ty)) |
113 | 6 | .render("decl", &["fixed_container"]) |
114 | 6 | .map_err(Error::Codec)?0 ; |
115 | 6 | Some(scope.var("type_name", type_name).render(function, &["array"]).unwrap()) |
116 | | } |
117 | 12 | FieldType::Container(v) => { |
118 | 12 | scope |
119 | 12 | .var("item_type", type_path_map.get(&v.item_type)) |
120 | 12 | .var("codec", U::get_value_type(field.endianness, v.ty)); |
121 | 12 | let type_name = codec_template |
122 | 12 | .scope() |
123 | 12 | .var("item_type", type_path_map.get(&v.item_type)) |
124 | 12 | .var("codec", U::get_value_type(field.endianness, v.ty)) |
125 | 12 | .render("decl", &["container"]) |
126 | 12 | .map_err(Error::Codec)?0 ; |
127 | 12 | Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap()) |
128 | | } |
129 | 4 | FieldType::SizedContainer(v) => { |
130 | 4 | scope |
131 | 4 | .var("item_type", type_path_map.get(&v.item_type)) |
132 | 4 | .var("codec", U::get_value_type(field.endianness, v.ty)); |
133 | 4 | let type_name = codec_template |
134 | 4 | .scope() |
135 | 4 | .var("item_type", type_path_map.get(&v.item_type)) |
136 | 4 | .var("codec", U::get_value_type(field.endianness, v.ty)) |
137 | 4 | .var("size_codec", U::get_value_type(field.endianness, v.size_ty)) |
138 | 4 | .render("decl", &["sized_container"]) |
139 | 4 | .map_err(Error::Codec)?0 ; |
140 | 4 | Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap()) |
141 | | } |
142 | 35 | _ => None, |
143 | | }; |
144 | 57 | Ok(value) |
145 | 57 | } |
146 | | |
147 | 34 | pub fn gen_message_array_type_decls<U: Utilities, T: TypeMapper>( |
148 | 34 | templates: &Templates, |
149 | 34 | function: &str, |
150 | 34 | msg: &Message, |
151 | 34 | type_path_map: &TypePathMapper<T>, |
152 | 34 | ) -> Result<String, Error> { |
153 | 34 | msg.fields |
154 | 34 | .iter() |
155 | 57 | .filter_map(|field| gen_message_array_field::<U, T>(templates, function, field, type_path_map).transpose()) |
156 | 34 | .collect::<Result<Vec<String>, Error>>() |
157 | 34 | .map(|v| v.join("")) |
158 | 34 | } |
159 | | |
160 | 28 | pub fn generate<'variable, U: Utilities, T: TypeMapper>( |
161 | 28 | mut templates: Templates<'_, 'variable>, |
162 | 28 | msg: &'variable Message, |
163 | 28 | type_path_map: &TypePathMapper<T>, |
164 | 28 | ) -> Result<String, Error> { |
165 | 28 | templates.template.var("msg_name", &msg.name); |
166 | 28 | templates.template.var( |
167 | 28 | "msg_description", |
168 | 28 | msg.description.as_ref().map(U::gen_description).unwrap_or("".into()), |
169 | 28 | ); |
170 | 28 | let fields = msg |
171 | 28 | .fields |
172 | 28 | .iter() |
173 | 49 | .map(|v| gen_msg_field_decl::<U, T>(v, &templates, type_path_map)) |
174 | 28 | .collect::<Result<Vec<String>, Error>>()?0 |
175 | 28 | .join(""); |
176 | 28 | let mut code = templates.template.scope().var("fields", fields).render("", &["decl"]).unwrap(); |
177 | 28 | code += "\n"; |
178 | 28 | code += &gen_message_array_type_decls::<U, T>(&templates, "typealias", msg, type_path_map)?0 ; |
179 | 28 | Ok(code) |
180 | 28 | } |